package qc.module.iot.api.channel.handler;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qc.common.core.enums.CommunicationChannelTypeEnum;
import qc.common.core.enums.iot.IotPacketsDataProtocolEnum;
import qc.common.core.enums.iot.IotPacketsTransmitProtocolEnum;
import qc.common.core.utils.RadixConvertUtil;
import qc.module.iot.protocol.IotFrameBodyDataItem;
import qc.module.iot.protocol.IotFrameParseBase;
import qc.module.iot.protocol.IotFrameParseResult;
import qc.module.iot.protocol.IotPacketsSplitResult;
import qc.module.iot.protocol.custom.Custom_DeHongSwj_FrameParse;
import qc.module.iot.protocol.ddp.DDP_V30_FrameParse;
import qc.module.iot.protocol.modbus.Modbus_Lj_Ems_FrameParse;
import qc.module.iot.protocol.sl427.SL427_FrameParse;
import qc.module.iot.protocol.sl651.SL651_Ascii_FrameParse;
import qc.module.iot.protocol.sl651.SL651_Hex_FrameParse;
import qc.module.iot.service.IotChannelService;
import qc.module.iot.service.IotFramePacketsService;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List;

/**
 * 通道入站Handler基类，定义公用的属性和方法
 *
 * @author QuCheng Tech
 * @since 2025/11/4
 */
public class NettylInboundBaseHandler extends ChannelInboundHandlerAdapter {
    private static final Logger logger = LoggerFactory.getLogger(NettylInboundBaseHandler.class);

    /**
     * service，由于使用@Autowired注解得到的始终为null（原因未知），通过构造方法初始化时传入
     */
    protected final IotFramePacketsService framePacketsService;
    protected final IotChannelService channelService;
    /**
     * 数据帧报文是否先发送至MQ，如果为true先发送至MQ，如果发生至MQ失败再保存至数据库中；如果为false不发送至MQ，直接保存至数据库中；
     */
    protected final boolean packetsSendToMQ;
    /**
     * 通道ID
     */
    protected final String channelId;
    /**
     * 通道名称
     */
    protected final String channelName;
    /**
     * 通道类型
     */
    protected final CommunicationChannelTypeEnum channelType;
    /**
     * 是否使用UDP协议，确定网络通讯是TCP还是UDP
     */
    protected final boolean isUdp;
    /**
     * 服务端使用的端口
     */
    protected final int port;
    /**
     * 传输协议
     */
    protected final IotPacketsTransmitProtocolEnum transmitProtocol;
    /**
     * 数据协议
     */
    protected final IotPacketsDataProtocolEnum dataProtocol;
    /**
     * 传输帧解析类，根据使用的数据协议不同进行不同的初始化
     */
    protected IotFrameParseBase transmitFrameParse;
    /**
     * 数据帧解析类，根据使用的数据协议不同进行不同的初始化
     */
    protected IotFrameParseBase dataFrameParse;

    /**
     * 构造函数
     *
     * @param framePacketsService 数据帧Service
     * @param channelService      通信信道Service
     * @param packetsSendToMQ     数据帧报文是否先发送至MQ
     * @param channelId           通道ID
     * @param channelName         通道名称
     * @param channelType         通道类型
     * @param isUdp               是否使用UDP协议
     * @param port                端口
     * @param transmitProtocol    传输协议
     * @param dataProtocol        数据协议
     * @return
     * @author QuCheng Tech
     * @since 2025/11/5
     */
    public NettylInboundBaseHandler(IotFramePacketsService framePacketsService, IotChannelService channelService, boolean packetsSendToMQ,
                                    String channelId, String channelName, CommunicationChannelTypeEnum channelType, boolean isUdp,
                                    int port, IotPacketsTransmitProtocolEnum transmitProtocol, IotPacketsDataProtocolEnum dataProtocol) {
        logger.info("NettylInboundBaseHandler 通道[{}，]端口[{}]，传输协议[{}]，数据协议[{}]", channelName, port, transmitProtocol, dataProtocol);

        this.framePacketsService = framePacketsService;
        this.channelService = channelService;
        this.packetsSendToMQ = packetsSendToMQ;
        this.channelId = channelId;
        this.channelName = channelName;
        this.channelType = channelType;
        this.isUdp = isUdp;
        this.port = port;
        this.transmitProtocol = transmitProtocol;
        this.dataProtocol = dataProtocol;

        //根据不同的传输协议初始化不同的传输帧解析类
        if (this.transmitProtocol == IotPacketsTransmitProtocolEnum.DDP)
            this.transmitFrameParse = new DDP_V30_FrameParse();

        //根据不同的数据协议初始化不同的数据帧解析类
        if (this.dataProtocol == IotPacketsDataProtocolEnum.SL651_HEX)
            this.dataFrameParse = new SL651_Hex_FrameParse();
        else if (this.dataProtocol == IotPacketsDataProtocolEnum.SL651_ASCII)
            this.dataFrameParse = new SL651_Ascii_FrameParse();
        else if (this.dataProtocol == IotPacketsDataProtocolEnum.SL_427)
            this.dataFrameParse = new SL427_FrameParse();
        else if (this.dataProtocol == IotPacketsDataProtocolEnum.MODBUS_LJ_EMS)
            this.dataFrameParse = new Modbus_Lj_Ems_FrameParse();
        else if (this.dataProtocol == IotPacketsDataProtocolEnum.CUSTOM_DEHONG_SWJ)
            this.dataFrameParse = new Custom_DeHongSwj_FrameParse();
        else
            logger.warn("未实现的数据协议 信道名称[{}]，数据协议[{}]", channelName, dataProtocol);
    }

    /**
     * 通信信道激活事件，客户端建立连接时触发
     *
     * @param context 通信信道处理上下文
     * @return void
     * @author QuCheng Tech
     * @since 2025/11/5
     */
    @Override
    public void channelActive(ChannelHandlerContext context) throws Exception {
        String clientIp = this.getClientIp(context);
        int clientPort = this.getClientPort(context);

        logger.debug("建立与[{}]的{}连接 来源客户端[{}:{}]", this.port, this.isUdp ? "UDP" : "TCP", clientIp, clientPort);

        //更新最新通讯时间
        updateLatestCommunicateTime();

        // 检查连接限制
        //if (!nettyServer.isConnectionAllowed(clientIp, port)) {
        //    logger.info("警告: 连接数超限，拒绝连接: " + clientIp);
        //    context.close();
        //    return;
        //}
        // 注册连接
        //communicationChannelServers.registerConnection(getClientIp(context), port, context.channel());
        logger.info("建立与[{}]的{}连接成功。来源客户端[{}:{}]", this.port, this.isUdp ? "UDP" : "TCP", clientIp, clientPort);
        super.channelActive(context);
    }

    /**
     * 通信信道未激活事件，客户端断开连接时触发
     *
     * @param context 通信信道处理上下文
     * @return void
     * @author QuCheng Tech
     * @since 2025/11/5
     */
    @Override
    public void channelInactive(ChannelHandlerContext context) throws Exception {
        String clientIp = this.getClientIp(context);
        int clientPort = this.getClientPort(context);

        logger.info("关闭与[{}]的{}连接 来源客户端[{}:{}]", this.port, this.isUdp ? "UDP" : "TCP", clientIp, clientPort);

        if (StringUtils.isNotBlank(clientIp) && this.isPortValid(clientPort) == true) {
            //communicationChannelServers.unregisterConnection(clientIp, port, context.channel());
        }
        super.channelInactive(context);
    }

    /**
     * 通信信道读取数据事件，客户端发送数据时触发（接收端收到数据时触发）
     *
     * @param context 通信信道处理上下文
     * @param msg     收到的消息/数据
     * @return void
     * @author QuCheng Tech
     * @since 2025/11/5
     */
    @Override
    public void channelRead(ChannelHandlerContext context, Object msg) {
        String clientIp = this.getClientIp(context);
        int clientPort = this.getClientPort(context);

        logger.debug("收到向[{}]的{}发送[{}]数据 来源客户端[{}:{}]", this.port, this.isUdp ? "UDP" : "TCP",
                this.transmitProtocol, clientIp, clientPort);

        ByteBuf readByteBuf = (ByteBuf) msg;
        try {
            int readLength = readByteBuf.readableBytes();
            //读取数据
            byte[] readBytes = new byte[readLength];
            readByteBuf.readBytes(readBytes);

            logger.info("收到向[{}]的{}发送[{}]数据 来源客户端[{}:{}]，长度[{}]，内容[{}]", this.port, this.isUdp ? "UDP" : "TCP",
                    this.transmitProtocol, clientIp, clientPort, readLength, RadixConvertUtil.toHexString(readBytes));

            //收到数据后根据使用的数据协议进行分解出有效报文
            List<IotPacketsSplitResult> splitResult = this.dataFrameParse.split(readBytes);
            if (splitResult == null || splitResult.size() < 0x1) {
                //没有分解出任何可能的数据帧
                logger.info("收到报文内容[{}]中没有分解出任何可能的数据帧。", RadixConvertUtil.toHexString(readBytes));
            } else {
                //遍历分解出的可能的数据帧进行解析
                for (IotPacketsSplitResult splitPackets : splitResult) {
                    byte[] splitFrameBytes = splitPackets.getBytes();
                    //logger.debug("收到报文分解出的数据帧:[{}]", RadixConvertUtil.toHexString(splitFrameBytes));
                    try {
                        IotFrameParseResult frameParseResult = this.dataFrameParse.parse(splitFrameBytes, false);//不解析报文正文内容
                        //IotFrameParseResult frameParseResult = this.dataFrameParse.parse(splitFrameBytes, true);//完全解析，包含解析报文正文内容
                        if (frameParseResult.isParseSuccess() == false) {
                            logger.info("解析数据帧失败，报文[{}]，解析错误信息[{}]", RadixConvertUtil.toHexString(splitFrameBytes), frameParseResult.getParseErrorMsg());
                        } else {
                            logger.info("解析数据帧成功，帧类型[{}]，帧名称[{}]，报文[{}]，完整数据帧报文[{}]", frameParseResult.getFrameType(),
                                    frameParseResult.getFrameName(), RadixConvertUtil.toHexString(splitFrameBytes), RadixConvertUtil.toHexString(frameParseResult.getParseFrameBytes()));

                            //显示解析出的正文数据
                            List<IotFrameBodyDataItem> bodyDataItems = frameParseResult.getBodyDataItems();
                            if (bodyDataItems != null && bodyDataItems.size() > 0x0) {
                                logger.debug("解析数据帧成功，共有正文内容项" + bodyDataItems.size() + "个。");
                                for (IotFrameBodyDataItem item : bodyDataItems) {
                                    logger.debug("类型：" + item.getType() + "，名称：" + item.getName() + "，值：" + item.getValue().toString() + "，编码：" + item.getCode());
                                }
                            }

                            //调用数据帧报文记录接口发送至MQ或保存至数据库
                            try {
                                String receiveResult = framePacketsService.receive(this.channelId, this.channelName, this.channelType, this.packetsSendToMQ,
                                        frameParseResult.getParseFrameBytes(), this.dataProtocol, frameParseResult.getFrameType(),
                                        frameParseResult.getFrameName(), frameParseResult.getStationAddress(), frameParseResult.getDataTime());
                                if (StringUtils.isBlank(receiveResult)) {
                                    //成功
                                    logger.debug("保存数据帧时成功，报文[{}]", RadixConvertUtil.toHexString(splitFrameBytes));
                                } else {
                                    //失败
                                    logger.warn("保存数据帧失败，报文[{}]，提示信息[{}]", RadixConvertUtil.toHexString(splitFrameBytes), receiveResult);
                                }
                            } catch (Exception ex) {
                                logger.error("保存数据帧时异常，报文[{}]，异常信息[{}]", RadixConvertUtil.toHexString(splitFrameBytes), ex.getMessage());
                            }

                            //解析数据帧成功后调用数据帧解析类中的生成回复方法，如果得到的回复字节数组不为空表示需要进行回复，发送对应的回复报文
                            try {
                                byte[] replayFrameBytes = this.dataFrameParse.generateReplyFrame(frameParseResult);
                                if (replayFrameBytes != null && replayFrameBytes.length > 0x0) {
                                    ByteBuf responseBuf = Unpooled.wrappedBuffer(replayFrameBytes);
                                    context.writeAndFlush(responseBuf).addListener(future -> {
                                        if (future.isSuccess()) {
                                            logger.info("由[{}]的{}向客户端[{}:{}]发送[{}]回复报文成功，长度[{}]，内容[{}]", this.port, this.isUdp ? "UDP" : "TCP",
                                                    clientIp, clientPort, this.transmitProtocol, replayFrameBytes.length, RadixConvertUtil.toHexString(replayFrameBytes));
                                        } else {
                                            logger.error("由[{}]的{}向客户端[{}:{}]发送[{}]回复报文失败，长度[{}]，内容[{}]", this.port, this.isUdp ? "UDP" : "TCP",
                                                    clientIp, clientPort, this.transmitProtocol, replayFrameBytes.length, RadixConvertUtil.toHexString(replayFrameBytes));
                                        }
                                    });
                                }
                            } catch (Exception ex) {
                                logger.error("由[{}]的{}向客户端[{}:{}]进行回复处理时异常，异常信息[{}]", this.port, this.isUdp ? "UDP" : "TCP", clientIp, clientPort, ex.getMessage());
                            }
                        }
                    } catch (Exception ex) {
                        logger.info("解析数据帧时异常，报文[{}]，异常信息[{}]", RadixConvertUtil.toHexString(splitFrameBytes), ex.getMessage());
                    }
                }
            }

            //如果通信信道中有收到报文（不管是否正确、是否有效）均判断为是一次接收到数据
            updateLatestReceivePacketsTime();
        } catch (Exception e) {
            logger.error("收到向[{}]的{}发送[{}]数据，处理收到数据时异常。来源客户端[{}:{}]，异常信息[]", this.port, this.isUdp ? "UDP" : "TCP",
                    this.transmitProtocol, clientIp, clientPort, e.getMessage());
        } finally {
            // 确保释放ByteBuf
            if (readByteBuf.refCnt() > 0) {
                readByteBuf.release();
            }
        }
    }

    /**
     * 用于捕获通道异常并清理资源
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
        String clientIp = this.getClientIp(context);
        int clientPort = this.getClientPort(context);

        logger.error("捕获到[{}]的{}异常 来源客户端[{}:{}]，异常信息[{}]", this.port, this.isUdp ? "UDP" : "TCP", clientIp, clientPort, cause);

        context.close();
    }

    /**
     * 处理客户端读空闲事件，通过关闭超时连接
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext context, Object event) {
        String clientIp = this.getClientIp(context);
        int clientPort = this.getClientPort(context);
        String eventType = null;
        if (event != null)
            eventType = event.getClass().getTypeName();

        logger.error("触发到[{}]的{}用户事件 来源客户端[{}:{}]，事件类型[{}]", this.port, this.isUdp ? "UDP" : "TCP", clientIp, clientPort, eventType);

        if (event instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) event;
            if (idleStateEvent.state() == IdleState.READER_IDLE) {
                logger.error("触发到[{}]的{}用户事件，读取超时，关闭连接。来源客户端[{}:{}]", this.port, this.isUdp ? "UDP" : "TCP", clientIp, clientPort);

                context.close();
            }
        }
    }

    /**
     * 获取客户端的完整网络地址
     */
    protected String getClientAddress(ChannelHandlerContext context) {
        if (context != null && context.channel() != null && context.channel().remoteAddress() != null) {
            SocketAddress remoteAddress = context.channel().remoteAddress();

            InetSocketAddress address = (InetSocketAddress) remoteAddress;
            if (address != null && address.getAddress() != null)
                return address.getAddress().getHostAddress() + ":" + address.getPort();
        }

        return null;
    }

    /**
     * 获取客户端的 IP 地址
     */
    protected String getClientIp(ChannelHandlerContext context) {
        if (context != null && context.channel() != null && context.channel().remoteAddress() != null) {
            SocketAddress remoteAddress = context.channel().remoteAddress();

            InetSocketAddress address = (InetSocketAddress) remoteAddress;
            if (address != null && address.getAddress() != null)
                return address.getAddress().getHostAddress();
        }

        return null;
    }

    /**
     * 获取客户端的端口号
     */
    protected int getClientPort(ChannelHandlerContext context) {
        if (context != null && context.channel() != null && context.channel().remoteAddress() != null) {
            SocketAddress remoteAddress = context.channel().remoteAddress();

            InetSocketAddress address = (InetSocketAddress) remoteAddress;
            if (address != null)
                return address.getPort();
        }

        return 0x0;
    }

    /**
     * 获取客户端的端口号
     */
    protected boolean isPortValid(int port) {
        //端口号必须在1~65536之间
        if (port <= 0x0)
            return false;
        if (port > 0x10000)
            return false;

        return true;
    }

    /**
     * 更新指定通道的最新通讯时间，最新通讯包括建立连接、发送心跳/注册报文
     *
     * @return void
     * @author QuCheng Tech
     * @since 2025/11/19
     */
    void updateLatestCommunicateTime() {
        try {
            String updateResult = this.channelService.updateLatestCommunicateTime(this.channelId);

            if (StringUtils.isNotBlank(updateResult))
                logger.warn("更新通道[{}]最新通讯时间失败，提示信息[{}]", this.channelId, updateResult);
        } catch (Exception ex) {
            logger.warn("更新通道[{}]最新通讯时间异常，异常信息[{}]", this.channelId, ex.getMessage());
        }
    }

    /**
     * 更新指定通道的最新接收报文时间(同时更新最新通讯时间)
     *
     * @return void
     * @author QuCheng Tech
     * @since 2025/11/19
     */
    void updateLatestReceivePacketsTime() {
        try {
            String updateResult = this.channelService.updateLatestReceivePacketsTime(this.channelId);

            if (StringUtils.isNotBlank(updateResult))
                logger.warn("更新通道[{}]最新收到报文时间失败，提示信息[{}]", this.channelId, updateResult);
        } catch (Exception ex) {
            logger.warn("更新通道[{}]最新收到报文时间异常，异常信息[{}]", this.channelId, ex.getMessage());
        }
    }
}
